var net = require('net');

var local_port = 8888;

net.createServer({allowHalfOpen: false}, function(client){
    //异步请求，所以需要等待完整的request包才处理对应的连接
    var buffer = new Buffer(0);

    client.on('data', function(data) {

        buffer = bufferCombine(buffer, data);

        if(posHttpRequestBody(buffer) == -1) {
            return;
        }

        var request = parseBuffer(buffer);

        if(request === false){
             return;
        }

        //得到连接请求后，删除'data'事件
        //以后的'data'事件由于是客户端长连接
        client.removeAllListeners('data');
        //取得连接后，剩下的事情由proxy 处理
        proxyServer(request);

    });

    function proxyServer(req){
        if(req.method != 'CONNECT'){
            console.log(req.method + ' ' + req.host + ':' + req.port + ' path:' + req.path);

            var _body_pos = posHttpRequestBody(buffer);

            if(_body_pos < 0) _body_pos = buffer.length;

            var header = buffer.slice(0, _body_pos).toString('utf8');

            header = header.replace(/(proxy\-)?connection\:.+\r\n/ig, '')

            .replace(/Keep\-Alive\:.+\r\n/i, '')

            .replace("\r\n", '\r\nConnection: close\r\n');

            if(req.httpVersion == '1.1'){

                var url = req.path.replace(/http\:\/\/[^\/]+/, '');

                if(url.path != url) header = header.replace(req.path, url);

            }

            buffer = bufferCombine(new Buffer(header, 'utf8'), buffer.slice(_body_pos));

        }

        //为此新连接建立remote的HTTP长连接，这里由socket层代理
        var remote = net.createConnection(req.port, req.host);

        //最重要的部分
        //重新设置client的 'data'事件，并绑定处理函数为转发到目标服务器
        //这里的 remote 服务器建立的是长连接，与客户端建立的也是长连接
        client.on("data", function(data) {
            remote.write(data);
        });
        remote.on("data", function(data) {
            client.write(data);
        });

        client.on('end', function(){
            remote.end();
        });

        remote.on('end', function(){
            client.end();
        });

        if(req.method == 'CONNECT'){
            console.log(req.method + ' ' + req.host + ':' + req.port );
            client.write(new Buffer("HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n"));
        }else{
            remote.write(buffer);   
        }
    }
}).listen(local_port);



console.log('Proxy server running at localhost:' + local_port);

process.on('uncaughtException', function(err){
    console.log(err);
});

function parseBuffer(buffer){

    var s = buffer.toString('utf8');

    var method = s.split('\n')[0] ;
    if( method ){
        method = method.match(/^([A-Z]+)\s/)[1];
    }

    if(method == 'CONNECT'){

        var arr = s.match(/^([A-Z]+)\s([^\:\s]+)\:(\d+)\sHTTP\/(\d\.\d)/);

        if(arr && arr[1] && arr[2] && arr[3] && arr[4]){
            return {
                method:      arr[1],
                host:        arr[2],
                port:        arr[3],
                httpVersion: arr[4]
            };
        }

    }else{
        var arr = s.match(/^([A-Z]+)\s([^\s]+)\sHTTP\/(\d\.\d)/);

        if(arr && arr[1] && arr[2] && arr[3]){

            var host = s.match(/Host\:\s+([^\n\s\r]+)/)[1];

            if(host){
                var _p = host.split(':', 2);
                return {
                    method:      arr[1],
                    host:        _p[0],
                    port:        _p[1] || 80,
                    path:        arr[2],
                    httpVersion: arr[3]
                };
            }
        }
    }
    return false;
}

/**
 * 两个buffer对象加起来
 */

function bufferCombine(buf1, buf2){
    var re = new Buffer(buf1.length + buf2.length);
    buf1.copy(re);
    buf2.copy(re, buf1.length);
    return re;
}



/**
 * 从缓存中找到头部结束标记("\r\n\r\n")的位置
 */

function posHttpRequestBody(b){

    for(var i = 0, len = b.length - 3; i < len; i++){

        if(b[i] == 0x0d && b[i + 1] == 0x0a && b[i + 2] == 0x0d && b[i + 3] == 0x0a){
            return i + 4;
        }
    }
    return -1;
}